<link rel="import" href="../bower_components/app-route/app-route.html">
<link rel="import" href="../bower_components/paper-spinner/paper-spinner.html">
<link rel="import" href="../bower_components/mist-list/mist-list.html">
<link rel="import" href="../bower_components/paper-fab/paper-fab.html">
<link rel="import" href="helpers/owner-filter-behavior.html">

<link rel="import" href="machines/machine-create.html">
<link rel="import" href="machines/machine-page.html">
<link rel="import" href="machines/machine-actions.html">

<dom-module id="page-machines">
    <template>
        <style include="shared-styles">
            [hidden] {
                display: none !important;
            }

            .logs {
                max-width: 1450px;
                margin: 8px auto;
                padding: 0 8px;
                line-height: 28px;
                font-family: monospace;
                border: 1px solid #ddd;
                border-radius: 8px;
                padding: 0 8px;
                background-color: rgba(255, 255, 255, 0.34);
                display: flex;
                align-items: center
            }

            paper-spinner {
                margin: 4px 8px;
            }

            .logs > * {
                font-size: 14px;
            }

            .logs p {
                flex: 1;
            }

            .error iron-icon {
                opacity: 0.32;
                cursor: pointer;
            }

            .error {
                color: var(--red-color);
            }

            h2[slot="header"] {
                margin: 8px;
            }

            iron-icon.close {
                float: right;
                padding: 5px;
            }

            mist-list#machinesList {
                max-width: 95%;
            }
        </style>
        <app-route route="{{route}}" pattern="/:machine" data="{{data}}"></app-route>
        <div class="logs" hidden$="[[!showLogs]]">
            <paper-spinner active$="[[!logItem.error]]" hidden$="[[logItem.error]]"></paper-spinner>
            <p> Creating machine: {{removeUnderscore(logItem.action)}}
                <span class="error" hidden$="[[!logItem.error]]">[[logItem.error]]</span>
            </p>
            <iron-icon icon="close" on-tap="clearLog"></iron-icon>
        </div>
        <div class="logs" hidden$="[[!performingAction]]">
            <paper-spinner active></paper-spinner>
            <p id="actionLogs"></p>
            <iron-icon class="close" icon="close" on-tap="hidePerformingLogs"></iron-icon>
        </div>
        <template is="dom-if" if="[[_isListActive(route.path)]]" restamp>
            <machine-actions actions={{actions}} items=[[selectedItems]] model=[[model]]>
                <mist-list id="machinesList" selectable resizable column-menu multi-sort
                    sorters=[[sorters]] item-map="[[model.machines]]" name="Machines" selected-items={{selectedItems}}
                    filtered-items-length="{{filteredItemsLength}}" frozen=[[_getFrozenLogColumn()]] visible=[[_getVisibleColumns()]]
                    renderers=[[_getRenderers()]] route={{route}} user-filter=[[model.sections.machines.q]] primary-field-name="id"
                    actions=[[actions]] filter-method="[[_ownerFilter()]]" apiurl="/api/v1/machines">
                    <p slot="no-items-found">No machines found.</p>
                </mist-list>
            </machine-actions>
            <div class="absolute-bottom-right" hidden$="[[!check_perm('create','machine', null, model.org, model.user)]]">
                <paper-fab id="machinesAdd" icon="add" on-tap="_addResource"></paper-fab>
            </div>
        </template>
        <machine-create model="[[model]]" hidden$=[[!_isAddPageActive(route.path)]] monitoring=[[monitoring]] docs=[[docs]]></machine-create>
        <template is="dom-if" if="[[_isDetailsPageActive(route.path)]]" restamp>
            <machine-page path="[[route.path]]" model="[[model]]" machine="[[currentMachine]]" section="[[model.sections.machines]]" monitoring=[[monitoring]] user="[[model.user.id]]" hidden$=[[!_isDetailsPageActive(route.path)]] portal-name="[[portalName]]" resource-id="[[data.machine]]" currency="[[currency]]"></machine-page>
        </template>
        <iron-ajax id="getJobLog" method="GET" url="/api/v1/jobs/[[jobId]]" handle-as="json" on-response="handleGetJobLog" on-error="handleGetJobLogError"></iron-ajax>
    </template>
    <script>
    Polymer({
        is: 'page-machines',
        behaviors: [
            ownerFilterBehavior,
            rbacBehavior
        ],

        properties: {
            model: {
                type: Object
            },
            data: {
                type: Object
            },
            ownership: {
                type: Boolean
            },
            itemOfRecommendation: {
                type: Object
            },
            selectedItems: {
                type: Array
            },
            jobId: {
                type: String
            },
            logItem: {
                type: Object,
                value: false
            },
            intervalID: {
                type: String
            },
            performingAction: {
                type: Boolean,
                value: false
            },
            monitoring: {
                type: Boolean,
                value: false
            },
            actions: {
                type: Array
            },
            sorters: {
                type: Array,
                value: function() { return [
                        ['state', 'asc']
                    ]; }
            },
            currentMachine: {
                type: Object,
                computed: '_getMachine(data.machine, model.machines, model.machines.*)'
            },
            portalName: {
                type: String,
                value: 'Mist.io'
            },
            currency: {
                type: Object
            },
            showLogs: {
                type: Boolean,
                value: false
            }
        },

        listeners: {
            'open-recommendation-dialog': 'openRecommendationsDialog',
            'performing-action': 'updateActionLogs',
            'action-finished': 'stopActionLogs',
            'recommendation': 'openRecommendationsDialog',
            'set-job-id': 'setJobId'
        },

        observers: [
            '_jobIdChanged(jobId)',
            '_logItemChanged(logItem.*)'
        ],

        _getMachine: function(id, machines) {
            if (id && this.model && this.model.machines && this.model.machines[id])
                return this.model.machines[id];
        },

        setJobId: function(e) {
            // console.log('setJobId',e.detail)
            if (e.detail.jobId) {
                this.set('jobId', e.detail.jobId);
            } else if (e.detail.job_id) {
                this.set('jobId', e.detail.job_id);
            }
        },

        _isAddPageActive: function(path) {
            return path == '/+create';
        },

        _isDetailsPageActive: function(path) {
            // console.log('load _isDetailsPageActive', path);
            if (path && path != '/+create') {
                if (this.shadowRoot && this.shadowRoot.querySelector('machine-page')) {
                    this.shadowRoot.querySelector('machine-page').updateState();
                }
                return true;
            } else {
                return false;
            }
        },

        _isListActive: function(path) {
            return !path;
        },

        _shell: function(event) {
            var e = Polymer.dom(event);
            var action = {
                'name': 'shell',
                'icon': 'vaadin:terminal',
                'confirm': false,
                'multi': true
            }
            if (this.caller == 'machine_page') {
                this.$.actions_machine._performAction(action, [this.itemOfRecommendation]);
            } else {
                this.$.actions.performMachineAction(action, [this.itemOfRecommendation]);
            }
        },

        _jobIdChanged: function(jobid) {
            // console.log('_jobIdChanged', jobid);
            if (jobid == false) {
                this.stopPolling();
            } else if (jobid && jobid.length) {
                this.startPolling(jobid);
            }
        },

        _showLogs: function(val) {
            this.set('showLogs', val);
        },

        startPolling: function(jobid) {
            if (jobid) {
                this._showLogs(true);
                this.intervalID = setInterval(function() {
                    this.$.getJobLog.generateRequest();
                }.bind(this), 1000);
            }
            // console.log('startPolling');
        },

        stopPolling: function() {
            // console.log('stopPolling');
            this.set('jobId', false);
            if (this.intervalID != false) {
                window.clearInterval(this.intervalID);
                this.set('intervalID', false);
            }
        },

        _logItemChanged: function (logItem) {
            // If a log's action mentions `finished`, then stop polling.
            // console.log('_logItemChanged');
            if (this.logItem && this.logItem.action && this.logItem.action.endsWith('finished')) {
                this.stopPolling();
                // Hide logs if no errors occure.
                if (!this.logItem.error) {
                    this._showLogs(false);
                }
            }
        },

        handleGetJobLog: function(e) {
            // console.log('handleGetJobLog');
            // Save last log in logItem
            this.set('logItem', e.detail.response.logs[e.detail.response.logs.length - 1]);
        },

        handleGetJobLogError: function(e) {
            // console.log('handleGetJobLogError', e.detail);
            // Save last log in logItem - if it exists - and stop polling
            this.set('logItem', {error: e.detail.error.message + ' (request: /api/v1/jobs/' + this.jobId + ')'});
            this.stopPolling();
        },

        removeUnderscore: function(action) {
            if (action)
                return action.replace(/_/g, " ");
        },

        clearLog: function(e) {
            this.stopPolling();
            this._showLogs(false);
            this.set('logItem', {});
        },

        updateActionLogs: function(e) {
            this.set('performingAction', true);
            this.$.actionLogs.textContent = e.detail.log;
        },
        stopActionLogs: function(e) {
            var success = e.detail.success;
            this.$.actionLogs.textContent = '';
            this.set('performingAction', false);
            if (success)
                this.clearListSelection();
        },

        hidePerformingLogs: function(e) {
            this.set('performingAction', false);
        },

        clearListSelection: function() {
            this.set('selectedItems', []);
        },

        _addResource: function(e) {
            this.dispatchEvent(new CustomEvent('go-to', { bubbles: true, composed: true, detail: {
                url: this.model.sections.machines.add
            } }));
        },

        _getFrozenLogColumn: function() {
            return ['name'];
        },

        _getVisibleColumns: function() {
            var ret = ['state', 'cloud', 'cost', 'created', 'expiration', 'created_by', 'tags', 'image_id', 'size', 'location', 'parent', 'hostname', 'public_ips'];
            if (this.model.org && this.model.org.ownership_enabled == true)
                ret.splice(ret.indexOf('created_by'), 0, 'owned_by');
            return ret;
        },

        _getRenderers: function() {
            var _this = this;
            return {
                'indicator': {
                    'body': function(item, row) {
                        var green = "#69b46c",
                            pending = "#eee",
                            red = "#d96557",
                            color = 'transparent';
                        // 'background:  repeating-linear-gradient(-45deg,#ddd,#ddd 2px,#eee 2px,#eee 4px);'
                        if (row.monitoring && row.monitoring.hasmonitoring) {
                            color = green;
                            if (_this._machineHasIncidents(row, _this.model.incidentsArray))
                                color = red;
                            if (row.monitoring.installation_status == "installing" || !row.monitoring
                                .installation_status == "installing" || !row.monitoring.installation_status
                                .activated_at)
                                color = pending;
                            return 'border-left: 8px solid ' + color + '; padding-left: 8px;';
                        }
                        return 'border-left: 8px solid transparent; padding-left: 8px;';
                    }
                },
                'icon': {
                    'body': function(item, row) {
                        if (!_this.model.clouds[row.cloud])
                            return '';
                        if (_this.model.clouds[row.cloud].provider == 'libvirt' && row.parent) {
                            return './assets/providers/kvm.png';
                        }
                        return './assets/providers/provider-' + _this.model.clouds[row.cloud].provider.replace("_", "")
                            .replace(" ", "") + '.png';
                    }
                },
                'name': {
                    'body': function(item, row) {
                        return '<strong class="name">' + item + '</strong>';
                    }
                },
                'state': {
                    'body': function(item, row) {
                        var ret = '',
                            prefix = '';
                        if (_this.itemRecommendation(row)) {
                            prefix =
                                '<iron-icon icon="icons:report-problem" class="recommendation-icon"></iron-icon>';
                        }
                        if (item == "running")
                            ret += "<div class='state " + _this.itemProbeClasses(row) +
                            "'><span class='green'>" + item + "</span></div>";
                        else if (item == "error")
                            ret += "<div class='state " + _this.itemProbeClasses(row) +
                            "'><span class='error'>" + item + "</span></div>";
                        else if (item == "stopped")
                            ret += "<div class='state " + _this.itemProbeClasses(row) +
                            "'><span class='orange'>" + item + "</span></div>";
                        else
                            ret += "<div class='state'>" + item + "</div>";

                        return prefix + ret;
                    },
                    'cmp': function(item1, item2, row1, row2) {
                        if (row1.monitoring && !row2.monitoring) {
                            return -1;
                        } else if (!row1.monitoring && row2.monitoring) {
                            return 1;
                        }
                        return item1.localeCompare(item2);
                    }
                },
                'cloud': {
                    'body': function(item, row) {
                        if (_this.model && _this.model.clouds)
                            return _this.model.clouds[item] ? _this.model.clouds[item].title :
                                '';
                    },
                    'cmp': function(item1, item2, row1, row2) {
                        if (_this.model && _this.model.clouds && _this.model.clouds[item1.id] &&
                            _this.model.clouds[item2.id]) {
                            if (_this.model.clouds[item1.id].title < _this.model.clouds[item2.id].title)
                                return -1;
                            if (_this.model.clouds[item1.id].title > _this.model.clouds[item2.id].title)
                                return 1;
                        }
                        return 0;
                    }
                },
                'parent': {
                    'body': function(item, row) {
                        if (item && _this.model && _this.model.machines && _this.model.machines[item]) {
                            return '<a href="/machines/' + item + '">' + _this.model.machines[item].name + '</a>';
                        } else {
                            return item;
                        }
                    }
                },
                'cost': {
                    'body': function(item, row) {
                        return item && item.monthly && _this.currency? _this.currency.sign + _this._ratedCost(parseFloat(item.monthly), _this.currency.rate) : '';
                    },
                    'cmp': function(item1, item2, row1, row2) {
                        if (item1.monthly < item2.monthly)
                            return -1;
                        if (item1.monthly > item2.monthly)
                            return 1;
                        return 0;
                    }
                },
                'owned_by': {
                    'title': function(item, row) {
                        return 'owner';
                    },
                    'body': function(item, row) {
                        return _this.model.members[item] ? _this.model.members[item].name || _this.model.members[item].email || _this.model.members[item].username : '';
                    }
                },
                'created_by': {
                    'title': function(item, row) {
                        return 'created by';
                    },
                    'body': function(item, row) {
                        return _this.model.members[item] ? _this.model.members[item].name || _this.model.members[item].email || _this.model.members[item].username : '';
                    }
                },
                'created': {
                    'body': function(item, row) {
                        return moment(item).isValid() ? moment.utc(item).fromNow() : "";
                    }
                },
                'size': {
                    'body': function(item, row) {
                        return _this.computeSize(row, item);
                    },
                    'cmp': function(item1, item2, row1, row2) {
                        var s1 = _this.computeSize(row1, item1),
                            s2 = _this.computeSize(row2, item2);

                        if (!s1.length && !s2.length)
                            return 0;
                        if (!s1.length)
                            return 1;
                        if (!s2.length)
                            return -1;

                        if (s1.toLowerCase() < s2.toLowerCase())
                            return -1;
                        if (s1.toLowerCase() > s2.toLowerCase())
                            return 1;
                        return 0;

                    }
                },
                'image_id': {
                    'title': function(item, row) {
                        return 'image';
                    },
                    'body': function(item, row) {
                        return _this._computeImage(row, item);
                    },
                    'cmp': function(item1, item2, row1, row2) {
                        var im1 = _this._computeImage(row1, item1),
                            im2 = _this._computeImage(row2, item2);

                        if (!im1.length && !im2.length)
                            return 0;
                        if (!im1.length)
                            return 1;
                        if (!im2.length)
                            return -1;

                        if (im1.toLowerCase() < im2.toLowerCase())
                            return -1;
                        if (im1.toLowerCase() > im2.toLowerCase())
                            return 1;
                        return 0;
                    }
                },
                'expiration': {
                    'title': function(item, row) {
                        return 'expiration';
                    },
                    'body': function(item, row) {
                        return item && item.date ? moment.utc(item.date).fromNow() : '';
                    },
                    'cmp': function(item1, item2, row1, row2) {
                        var exp1 = item1 && item1.date ? moment(item1.date) : moment(''),
                            exp2 = item2 && item2.date ? moment(item2.date) : moment('');

                        if (!exp1.isValid() && !exp2.isValid())
                            return 0;
                        if (!exp1.isValid())
                            return 1;
                        if (!exp2.isValid())
                            return -1;

                        if (exp1.isBefore(exp2))
                            return -1;
                        else if (exp1.isAfter(exp2))
                            return 1;
                        return 0;
                    }
                },
                'location': {
                    'body': function(item, row) {
                        if (_this.model && _this.model.clouds && _this.model.clouds[row.cloud] &&
                            _this.model.clouds[row.cloud].locations)
                            var location = _this.model.clouds[row.cloud].locations[item];
                        return location ? location.name : item || '';
                    }
                },
                'tags': {
                    'body': function(item, row) {
                        var tags = item,
                            display = "";
                        for (key in tags) {
                            display += "<span class='tag'>" + key;
                            if (tags[key] != undefined && tags[key] != "")
                                display += "=" + tags[key];
                            display += "</span>";
                        }
                        return display;
                    }
                },
                'machine_id': {
                    'title': 'id (external)',
                    'body': function(i) {
                        return i;
                    }
                },
                'public_ips': {
                    'title': 'public ip\'s',
                    'body': function(ips) {
                        return ips && ips.join(', ');
                    }
                },
                'private_ips': {
                    'title': 'private ip\'s',
                    'body': function(ips) {
                        return ips.join(', ');
                    }
                },
                'hostname': {
                    'body': function(hostname) {
                        return hostname || '';
                    }
                }
            }
        },

        _ratedCost: function(cost, rate) {
            return ratedCost(cost, rate);
        },

        _computeImage: function(row, item) {
            // FIXME This needs to be standarized in the backend to remove the cruft below
            var image_id = item;

            if (!image_id && row.extra && row.extra.image) {
                if (row.extra.image.distribution && row.extra.image.name) {
                    return row.extra.image.distribution + " " + row.extra.image.name;
                }
                image_id = row.extra.image;
            }
            if (!image_id && row.extra) {
                image_id = row.extra['image_id'] || row.imageId || (row.extra.image && (row.extra.image
                    .slug || row.extra.image.name));
            }

            if (image_id && row.cloud && this.model.clouds[row.cloud] && this.model.clouds[row.cloud
                    ].images && this.model.clouds[row.cloud].images[image_id]) {
                return this.model.clouds[row.cloud].images[image_id].name
            }

            return image_id || "";
        },

        computeSize: function(row, item) {
            // FIXME This needs to be standarized in the backend to remove the cruft below
            var size_id = item;


            // Try to figure out size_id
            if (row.size && typeof(row.size) != 'object') {
                size_id = row.size || '';
            }

            if (!size_id && row.extra) {
                if (row.extra.size && typeof(row.extra.size) == 'string') {
                    size_id = row.extra.size;
                } else {
                    size_id = row.extra.instance_type || row.extra.instance_size || row.extra.service_type ||
                        row.extra.PLANID;
                }
            }

            // Given size_id, try to figure out actual size name
            if (size_id && this.model.clouds && this.model.clouds[row.cloud.id] && this.model.clouds[
                    row.cloud.id].sizes && this.model.clouds[
                    row.cloud.id].sizes[size_id]) {
                var size = this.model.clouds[row.cloud.id].sizes[size_id];
                return size.name || size.id;
            }

            // If that fails look for size info in the extra metadata
            if (row.extra) {
                if (row.extra.size && row.extra.size.vcpus) {
                    var size_name = row.extra.size.vcpus + 'vCPU';
                    if (row.extra.size.memory)
                        size_name += ', ' + row.extra.size.memory + 'MB RAM';
                    return size_name;
                } else if (row.extra.maxCpu) {
                    size_name = row.extra.maxCpu + 'vCPU';
                    if (row.extra.maxMemory)
                        size_name += ', ' + row.extra.maxMemory + 'MB RAM';
                    return size_name;
                }
            }

            return size_id || '';
        },

        _getMachineWeight: function(machine, model) {
            var weight = 0;
            var machineHasIncidents = this._machineHasIncidents(machine, this.model.incidentsArray),
                machineHasMonitor = this._machineHasMonitoring(machine),
                machineHasrecommendations = this._machineHasrecommendations(machine),
                machineHasProbe = this._machineHasProbe(machine);
            machineState = this._machineState(machine);
            weight = machineHasIncidents + machineHasMonitor + machineHasrecommendations +
                machineHasProbe + machineState;
            return weight != NaN ? weight : 0;
        },

        _machineHasIncidents: function(machine, incidents) {
            var machineIncidents = incidents ? incidents.filter(function(inc) {
                return inc.machine_id == machine.machine_id && inc.cloud_id == machine.cloud && !inc.finished_at
            }) : [];
            return machineIncidents ? machineIncidents.length * 1000 : 0;
        },

        _machineHasMonitoring: function(machine) {
            return machine.monitoring && machine.monitoring.hasmonitoring ? 100 : 0;
        },

        _machineHasrecommendations: function(machine, probes) {
            return machine.probe && machine.probe.ssh && machine.probe.ssh.dirty_cow ? 10 : 0;
        },

        _machineHasProbe: function(machine) {
            return machine.probe && machine.probe.ssh && machine.probe.ssh.loadloadavg ? machine.probe.ssh
                .loadloadavg[0] + machine.probe.ssh.loadloadavg[1] + machine.probe.ssh.loadloadavg[2] :
                1;
        },

        _machineState: function(machine) {
            if (machine.state == 'running')
                return 5;
            if (machine.state == 'error')
                return 3;
            if (machine.state == 'stopped')
                return 2;
            if (machine.state == 'terminated')
                return 1;
            if (machine.state == 'unknown')
                return 0;
            return 0;
        },

        itemRecommendation: function(item) {
            if (this.probes == {} || !item || !item.id) {
                return false;
            } else {
                if (!this.model.probes[item.id] || !this.model.probes[item.id].dirty_cow)
                    return false;
                return true;
            }
        },

        itemProbeClasses: function(item) {
            if (this.probes == {}) {
                return '';
            } else {
                if (!this.model.probes[item.id] || !this.model.probes[item.id].loadavg) {
                    return "";
                } else {
                    var probe = this.model.probes[item.id].loadavg;
                    var cores = parseInt(this.model.probes[item.id].cores);
                    var classes = '';
                    var prefix = '';

                    classes += this.loadToColor(parseFloat(probe[0] / cores), "short");
                    classes += this.loadToColor(parseFloat(probe[1] / cores), "mid");
                    classes += this.loadToColor(parseFloat(probe[2] / cores), "long");

                    //has probe data
                    if (classes != "")
                        classes += "hasprobe "

                    return classes;
                }
            }
        },

        loadToColor: function(load, prefix) {
            if (load > 1.2)
                return prefix + "high ";
            else if (load > 0.8)
                return prefix + "medium ";
            else if (load > 0.6)
                return prefix + "eco ";
            else if (load > 0.2)
                return prefix + "low ";
            else
                return prefix + "low ";
        }
    });
    </script>
</dom-module>